package com.meng.learn.shardingsphere;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.meng.learn.shardingsphere.constants.BaseDataConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;


/**
 * 数据源配置
 */
@Configuration
@EnableTransactionManagement
@Slf4j
public class ShardingDataSourcesConfig {


    @Bean(name = "bmsdb1")
    @ConfigurationProperties(prefix = "spring.datasource.druid.bmsdb1")
    DruidDataSource bmsdb1() {
        DruidDataSource build = DruidDataSourceBuilder.create().build();
        setCommonDbInfo(build);
        return build;
    }

    @Bean(name = "bmsdb2")
    @ConfigurationProperties(prefix = "spring.datasource.druid.bmsdb2")
    DruidDataSource bmsdb2() {
        DruidDataSource build = DruidDataSourceBuilder.create().build();
        setCommonDbInfo(build);
        return build;
    }

    @Bean(name = "bmsdb3")
    @ConfigurationProperties(prefix = "spring.datasource.druid.bmsdb3")
    DruidDataSource bmsdb3() {
        DruidDataSource build = DruidDataSourceBuilder.create().build();
        setCommonDbInfo(build);
        return build;
    }

    @Bean(name = "bmsdb4")
    @ConfigurationProperties(prefix = "spring.datasource.druid.bmsdb4")
    DruidDataSource bmsdb4() {
        DruidDataSource build = DruidDataSourceBuilder.create().build();
        setCommonDbInfo(build);
        return build;
    }

    /**
     *
     */
    private void setCommonDbInfo(DruidDataSource build) {
        /*build.setAsyncCloseConnectionEnable(true);
        build.setInitialSize(5);
        build.setMinIdle(5);
        build.setMaxActive(200);
        build.setMaxWait(60000);
        build.setTimeBetweenEvictionRunsMillis(60000);
        build.setMinEvictableIdleTimeMillis(300000);
        build.setTestWhileIdle(true);
        build.setTestOnBorrow(false);
        build.setTestOnReturn(false);
        build.setPoolPreparedStatements(true);
        build.setMaxPoolPreparedStatementPerConnectionSize(20);
        build.setValidationQuery("select 1");
        build.setValidationQueryTimeout(1000);*/
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyConfigure() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    @Primary
    public DataSource getDataSource() throws SQLException {
        // dataSourceMap 添加数据库
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put(BaseDataConstants.DB_NAME_DEFAULT, bmsdb4());

        // 查询数据源开关
        DataSource switchDataSource = dataSourceMap.get(BaseDataConstants.DB_NAME_DEFAULT);
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("switchDataSource", transactionFactory, switchDataSource);
       /* org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(environment);
        configuration.addMapper(DataSourceMapper.class);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);*/

        StringBuilder sb = new StringBuilder();
        Map<String, String> orgDbMap = new HashMap<>();
       /* List<CodeCategoryDetailEO> dbSource;
        try (SqlSession session = sqlSessionFactory.openSession()) {
            DataSourceMapper dataSourceMapper = session.getMapper(DataSourceMapper.class);
            List<CodeCategoryDetailEO> switchSource = dataSourceMapper.dataSourceSwitchQry();
            // 开关开启不分库
            if (switchSource != null && !switchSource.isEmpty()){
                dataSourceMap = null;
                switchDataSource = null;
                log.info("使用默认库");
                return bmsdb4();
            }

            dbSource = dataSourceMapper.dataDBSourceMapQry();
            for (CodeCategoryDetailEO eo : dbSource) {
                orgDbMap.put(eo.getName(), eo.getValue());
                sb.append("'");
                sb.append(eo.getName());
                sb.append("',");
            }
            sb.setLength(sb.length() -1);
        }*/

        dataSourceMap.put(BaseDataConstants.DB_NAME_ONE, bmsdb1());
        dataSourceMap.put(BaseDataConstants.DB_NAME_SECOND, bmsdb2());
        dataSourceMap.put(BaseDataConstants.DB_NAME_THREE, bmsdb3());
        dataSourceMap.put(BaseDataConstants.DB_NAME_DEFAULT, bmsdb4());

        // shardingRuleConfig
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();

        // 添加分片规则
        shardingRuleConfig.getTableRuleConfigs().add(getTableRuleCon(orgDbMap, sb,
                BaseDataConstants.SHARDING_TABLE_CALCULATE,
                BaseDataConstants.SHARDING_COLUMN_DEFAULT,
                BaseDataConstants.SHARDING_CALCULATE_NODES));
        shardingRuleConfig.getTableRuleConfigs().add(getTableRuleCon(orgDbMap, sb,
                BaseDataConstants.SHARDING_TABLE_IMPYCDATA,
                BaseDataConstants.SHARDING_COLUMN_DEFAULT,
                BaseDataConstants.SHARDING_IMPYCDATA_NODES));
        shardingRuleConfig.getTableRuleConfigs().add(getTableRuleCon(orgDbMap,sb,
                BaseDataConstants.SHARDING_TABLE_CALCULATE_INFO,
                BaseDataConstants.SHARDING_COLUMN_DEFAULT,
                BaseDataConstants.SHARDING_CALCULATE_INFO_NODES));
        shardingRuleConfig.getTableRuleConfigs().add(getTableRuleCon(orgDbMap,sb,
                BaseDataConstants.SHARDING_TABLE_CALCULATE_MANUAL,
                BaseDataConstants.SHARDING_COLUMN_DEFAULT,
                BaseDataConstants.SHARDING_CALCULATE_MANUAL_NODES));
        shardingRuleConfig.getTableRuleConfigs().add(getTableRuleCon(orgDbMap,sb,
                BaseDataConstants.SHARDING_TABLE_MANUAL_INFO,
                BaseDataConstants.SHARDING_COLUMN_DEFAULT,
                BaseDataConstants.SHARDING_MANUAL_INFO_NODES));

        shardingRuleConfig.getBindingTableGroups().add(BaseDataConstants.SHARDING_TABLE_CALCULATE);
        shardingRuleConfig.getBindingTableGroups().add(BaseDataConstants.SHARDING_TABLE_IMPYCDATA);
        shardingRuleConfig.getBindingTableGroups().add(BaseDataConstants.SHARDING_TABLE_CALCULATE_INFO);
        shardingRuleConfig.getBindingTableGroups().add(BaseDataConstants.SHARDING_TABLE_CALCULATE_MANUAL);
        shardingRuleConfig.getBindingTableGroups().add(BaseDataConstants.SHARDING_TABLE_MANUAL_INFO);

        // 没有配置分库策略的表指定默认数据库
        shardingRuleConfig.setDefaultDataSourceName(BaseDataConstants.DB_NAME_DEFAULT);
        // 设置默认的分库策略
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration(BaseDataConstants.SHARDING_COLUMN_DEFAULT, new DBPreciseShardingAlgorithm(orgDbMap)));

        Properties props = new Properties();
        props.put("sql.show ", true);

        DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, props);
        log.info("分库分表数据源创建成功");
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager() throws Exception {
        DataSourceTransactionManager txManager = new DataSourceTransactionManager();
        txManager.setDataSource(getDataSource());
        return txManager;
    }

    /**
     * 获取分表规则配置
     */
    private TableRuleConfiguration getTableRuleCon(Map<String, String> orgDbMap, StringBuilder sb,String tableName, String columnName, String tableNodes) {
        TableRuleConfiguration result = new TableRuleConfiguration(tableName, tableNodes + "{[" +sb.toString()+ "]}");
        result.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration(columnName, new DBPreciseShardingAlgorithm(orgDbMap)));
        result.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration(columnName, new TablePreciseShardingAlgorithm(tableName)));
        return result;
    }

    /**
     * 获取只分库规则配置
     */
    private TableRuleConfiguration getDBRuleCon(Map<String, String> orgDbMap, String tableName, String columnName, String tableNodes) {
        TableRuleConfiguration result = new TableRuleConfiguration(tableName, tableNodes);
        result.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration(columnName, new DBPreciseShardingAlgorithm(orgDbMap)));
        return result;
    }

}
